package com.lovely3x.uec.catcher;

import android.content.Context;
import android.content.pm.PackageManager;
import android.os.Build;
import android.os.Process;
import android.util.DisplayMetrics;


import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.Closeable;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintStream;
import java.io.UnsupportedEncodingException;
import java.net.HttpURLConnection;
import java.net.URL;

/**
 * 网络异常处理器
 * Created by lovely3x on 16-3-10.
 */
public class HTTPExceptionHandler implements ExceptionHandler {

    private static final String TAG = "HTTPExceptionHandler";
    private final String mReportAddress;

    final static Object done = new Object();

    public HTTPExceptionHandler(String reportAddress) {
        this.mReportAddress = reportAddress;
    }

    public void deleteFiles(String dir, String[] files) {
        for (String name : files) {
            File file = new File(dir, name);
            try {
                file.delete();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    @Override
    public boolean handleException(Context context, Throwable e, String dir, String[] crFiles) {

        /**
         * * 新增异常反馈
         * @param model 机型
         * @param os_version 系统版本
         * @param os_brand 系统牌子
         * @param os_display 显示器材质
         * @param os_height 机型高
         * @param os_width 机型宽
         * @param os_density 屏幕密度
         * @param app_name 程序名
         * @param app_version 程序版本
         * @param exception_reason 异常原因
         * @return
         */
        DisplayMetrics metrics = context.getResources().getDisplayMetrics();
        String deviceModel = Build.MODEL;
        String deviceBrand = Build.BOARD;
        String deviceVersion = String.valueOf(Build.VERSION.SDK_INT);
        String deviceDisplay = Build.DISPLAY;
        String deviceWidth = String.valueOf(metrics.widthPixels);
        String deviceHeight = String.valueOf(metrics.heightPixels);
        String deviceDensity = String.valueOf(metrics.density);
        String appVersion = getVersionCode(context);

        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        PrintStream ps = new PrintStream(baos);e.printStackTrace(ps);
        String exceptionReason = new String(baos.toByteArray());

        sendWithHttp(true, new String[]{
                "model", deviceModel,
                "os_brand", deviceBrand,
                "os_version", deviceVersion,
                "os_display", deviceDisplay,
                "os_width", deviceWidth,
                "os_height", deviceHeight,
                "os_density", deviceDensity,
                "app_name", context.getPackageName(),
                "app_version", appVersion,
                "exception_reason", exceptionReason,
        });
        deleteFiles(dir, crFiles);
        return true;
    }

    public void sendWithHttp(final boolean isLaunch, final String[] parameters) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    URL url = new URL(mReportAddress);
                    HttpURLConnection connections = (HttpURLConnection) url.openConnection();
                    connections.setRequestMethod("POST");
                    connections.setDoInput(true);
                    connections.setDoOutput(true);

                    StringBuilder sb = new StringBuilder();
                    for (int i = 0; i < parameters.length - 1; i += 2) {
                        String key = parameters[i];
                        String value = parameters[i + 1];
                        sb.append(key).append("=").append(value);
                        if (i + 2 != parameters.length) {
                            sb.append("&");
                        }
                    }
                    connections.getOutputStream().write(sb.toString().getBytes());
                    synchronized (done) {
                        done.notifyAll();
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                    if (!isLaunch){
                        System.exit(1);
                        Process.killProcess(Process.myPid());
                    }else{
                        synchronized (done) {
                            done.notifyAll();
                        }
                    }
                }
            }
        }).start();
        if (isLaunch) {
            synchronized (done) {
                try {
                    done.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }

    }

    public String getVersionCode(Context context) {
        try {
            return String.valueOf(context.getPackageManager().getPackageInfo(context.getPackageName(), PackageManager.GET_META_DATA).versionCode);
        } catch (PackageManager.NameNotFoundException e) {
            e.printStackTrace();
        }
        return null;
    }

    @Override
    public void reportOnLaunch(Context context, String dir, String[] crFiles) {
        for (String crFile : crFiles) {
            /**
             * * 新增异常反馈
             * @param model 机型
             * @param os_version 系统版本
             * @param os_brand 系统牌子
             * @param os_display 显示器材质
             * @param os_height 机型高
             * @param os_width 机型宽
             * @param os_density 屏幕密度
             * @param app_name 程序名
             * @param app_version 程序版本
             * @param exception_reason 异常原因
             * @return
             */
            DisplayMetrics metrics = context.getResources().getDisplayMetrics();
            String deviceModel = Build.MODEL;
            String deviceBrand = Build.BOARD;
            String deviceVersion = String.valueOf(Build.VERSION.SDK_INT);
            String deviceDisplay = Build.DISPLAY;
            String deviceWidth = String.valueOf(metrics.widthPixels);
            String deviceHeight = String.valueOf(metrics.heightPixels);
            String deviceDensity = String.valueOf(metrics.density);
            String appVersion = getVersionCode(context);
            String exceptionReason = readException(new File(dir, crFile).getAbsolutePath());

            sendWithHttp(false, new String[]{
                    "model", deviceModel,
                    "os_brand", deviceBrand,
                    "os_version", deviceVersion,
                    "os_display", deviceDisplay,
                    "os_width", deviceWidth,
                    "os_height", deviceHeight,
                    "os_density", deviceDensity,
                    "app_name", context.getPackageName(),
                    "app_version", appVersion,
                    "exception_reason", exceptionReason,
            });
        }
        deleteFiles(dir, crFiles);
    }

    public String readException(String filePath) {
        try {
            FileInputStream fis = new FileInputStream(filePath);
            StreamUtils.readToString(fis);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return "";
    }

    @Override
    public void handleSuccessful(Context context) {
        System.exit(1);
        Process.killProcess(Process.myPid());
    }

    /**
     * Created by lovely3x on 15-11-14.
     */
    public static class StreamUtils {

        /**
         * 关闭closeable对象,流对象都是closeable的
         *
         * @param closeable 需要关闭的对象
         * @return true or false 表示失败或成功
         */
        public static boolean close(Closeable closeable) {
            boolean result = true;
            try {
                if (closeable != null) {
                    closeable.close();
                }
            } catch (IOException e) {
                result = false;
                e.printStackTrace();
            }
            return result;
        }

        /**
         * 关闭一组可以关闭的对象
         *
         * @param failureContinue 关闭一个失败后是否继续
         * @param closeables      可关闭的一组对象
         */
        public static void close(boolean failureContinue, Closeable... closeables) {
            for (Closeable c : closeables) {
                if (!close(c)) {
                    if (!failureContinue) {
                        break;
                    }
                }
            }
        }

        /**
         * 关闭一组可以关闭的对象,在关闭其中一个失败后会继续关闭其他的
         *
         * @param closeables 可关闭的一组对象
         */
        public static void close(Closeable... closeables) {
            close(true, closeables);
        }


        /**
         * 从输入流中复制数据到输出流中
         *
         * @param is         输入流
         * @param os         输出流
         * @param bufferSize 缓冲区大小
         * @param needClose  赋值完成 是否需要关闭流
         * @return 成功或失败
         */
        public static boolean copy(InputStream is, OutputStream os, int bufferSize, boolean needClose) {

            boolean result = true;

            byte[] buffer = new byte[bufferSize <= 0 ? (1024 * 4) : bufferSize];
            int len = -1;

            try {
                while ((len = is.read(buffer)) != -1) {
                    os.write(buffer, 0, len);
                }
            } catch (IOException e) {
                result = false;
                e.printStackTrace();
            } finally {
                if (needClose) {
                    StreamUtils.close(is, os);
                }
            }

            return result;
        }

        /**
         * 读取字符串
         *
         * @param fis      输入流
         * @param encoding 编码
         * @return 读取的字符串
         */
        public static String readToString(FileInputStream fis, String encoding) {
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            copy(fis, baos, 1024 * 8, true);
            try {
                return new String(baos.toByteArray(), 0, baos.size(), encoding);
            } catch (UnsupportedEncodingException e) {
            }
            return "";
        }

        /**
         * 读取字符串
         *
         * @param fis 输入流
         * @return 读取的字符串
         */
        public static String readToString(FileInputStream fis) {
            return readToString(fis, "UTF-8");
        }

        /**
         * 读取为字节数组
         *
         * @param fis 输入流
         * @return 读取的结果数组
         */
        public static byte[] readToByte(InputStream fis) {
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            copy(fis, baos, 4 * 1024, true);
            return baos.toByteArray();
        }


        public static boolean writeByteToStream(OutputStream os, byte[] bys) {
            return copy(new ByteArrayInputStream(bys), os, 4 * 1024, true);
        }

        /**
         * 写入字符串到指定的输出流中
         *
         * @param os      输出流
         * @param content 需要写入的内容
         * @return 是否写入成功
         */
        public static boolean writeStringToStream(OutputStream os, String content) {
            return copy(new ByteArrayInputStream(content.getBytes()), os, 4 * 1024, true);
        }

        /**
         * 写入字符串到指定的输出流中
         *
         * @param os       输出流
         * @param encoding 编码
         * @param content  需要写入的内容
         * @return 是否写入成功
         */
        public static boolean writeStringToStream(OutputStream os, String content, String encoding) {
            try {
                return copy(new ByteArrayInputStream(content.getBytes(encoding)), os, 4 * 1024, true);
            } catch (UnsupportedEncodingException e) {
                e.printStackTrace();
                return false;
            }
        }
    }

}
